/* ****************************************************************** **
**    OpenSees - Open System for Earthquake Engineering Simulation    **
**          Pacific Earthquake Engineering Research Center            **
**                                                                    **
**                                                                    **
** (C) Copyright 1999, The Regents of the University of California    **
** All Rights Reserved.                                               **
**                                                                    **
** Commercial use of this program without express permission of the   **
** University of California, Berkeley, is strictly prohibited.  See   **
** file 'COPYRIGHT'  in main directory for information on usage and   **
** redistribution,  and for a DISCLAIMER OF ALL WARRANTIES.           **
**                                                                    **
** Developed by:                                                      **
**   Frank McKenna (fmckenna@ce.berkeley.edu)                         **
**   Gregory L. Fenves (fenves@ce.berkeley.edu)                       **
**   Filip C. Filippou (filippou@ce.berkeley.edu)                     **
**                                                                    **
** ****************************************************************** */

// Written: User-defined implementation for GPU-accelerated IncrementalIntegrator
// Purpose: This file contains a simplified implementation of GPU_IncrementalIntegrator.

#include "GPU_IncrementalIntegrator.h"
#include "GPU_FormUnbalance.h"
#include <AnalysisModel.h>
#include <LinearSOE.h>
#include <Vector.h>
#include <Channel.h>
#include <FEM_ObjectBroker.h>
#include <OPS_Globals.h>
#include <iostream>
#include <chrono>

// Constructor
GPU_IncrementalIntegrator::GPU_IncrementalIntegrator(int maxElements, int maxDOFs, int maxNodes)
    : IncrementalIntegrator(),
      gpuFormUnbalance(new GPU_FormUnbalance(maxElements, maxDOFs, maxNodes)),
      gpuEnabled(true),
      profilingEnabled(false),
      lastGPUTime(0.0),
      lastCPUTime(0.0),
      gpuCallCount(0),
      cpuCallCount(0),
      totalGPUTime(0.0),
      totalCPUTime(0.0)
{
}

// Destructor
GPU_IncrementalIntegrator::~GPU_IncrementalIntegrator()
{
    // gpuFormUnbalance is automatically deleted by unique_ptr
}

// Override formUnbalance to use GPU implementation
int GPU_IncrementalIntegrator::formUnbalance(void)
{
    startTiming();
    
    if (gpuEnabled && gpuFormUnbalance && shouldUseGPU()) {
        // Use GPU implementation
        int result = gpuFormUnbalance->formUnbalance(getAnalysisModel(), getLinearSOE());
        
        double time = stopTiming();
        lastGPUTime = time;
        gpuCallCount++;
        totalGPUTime += time;
        
        if (result < 0) {
            opserr << "GPU_IncrementalIntegrator::formUnbalance - ";
            opserr << "GPU implementation failed, falling back to CPU\n";
            return formUnbalanceCPU();
        }
        
        return result;
    } else {
        return formUnbalanceCPU();
    }
}

// CPU fallback implementation
int GPU_IncrementalIntegrator::formUnbalanceCPU(void)
{
    startTiming();
    
    // Call the base class implementation
    int result = IncrementalIntegrator::formUnbalance();
    
    double time = stopTiming();
    lastCPUTime = time;
    cpuCallCount++;
    totalCPUTime += time;
    
    return result;
}

// Configuration methods
void GPU_IncrementalIntegrator::enableProfiling(bool enable)
{
    profilingEnabled = enable;
    if (gpuFormUnbalance) {
        gpuFormUnbalance->enableProfiling(enable);
    }
}

// Performance monitoring
double GPU_IncrementalIntegrator::getLastGPUTime() const
{
    return lastGPUTime;
}

double GPU_IncrementalIntegrator::getLastCPUTime() const
{
    return lastCPUTime;
}

void GPU_IncrementalIntegrator::printPerformanceStats() const
{
    if (gpuCallCount > 0 || cpuCallCount > 0) {
        opserr << "\n--------- GPU_IncrementalIntegrator Performance Stats ---------\n";
        opserr << "GPU calls: " << gpuCallCount 
               << " (avg time: " << (gpuCallCount > 0 ? totalGPUTime/gpuCallCount : 0.0) << " ms)\n";
        opserr << "CPU calls: " << cpuCallCount 
               << " (avg time: " << (cpuCallCount > 0 ? totalCPUTime/cpuCallCount : 0.0) << " ms)\n";
        opserr << "Total GPU time: " << totalGPUTime << " ms\n";
        opserr << "Total CPU time: " << totalCPUTime << " ms\n";
        opserr << "Speedup: " << (cpuCallCount > 0 && gpuCallCount > 0 ? 
                                 (totalCPUTime/cpuCallCount)/(totalGPUTime/gpuCallCount) : 0.0) << "x\n";
        opserr << "------------------------------------------------------------\n";
    }
}

// Memory management
int GPU_IncrementalIntegrator::setMaxProblemSize(int maxElements, int maxDOFs, int maxNodes)
{
    if (gpuFormUnbalance) {
        gpuFormUnbalance.reset(new GPU_FormUnbalance(maxElements, maxDOFs, maxNodes));
        return 0;
    }
    return -1;
}

// Adaptive switching between GPU and CPU
bool GPU_IncrementalIntegrator::shouldUseGPU() const
{
    // Always use GPU if enabled (in a real implementation, this could be more sophisticated)
    return gpuEnabled;
}

// Timing utilities
void GPU_IncrementalIntegrator::startTiming()
{
    if (profilingEnabled) {
        startTime = std::chrono::high_resolution_clock::now();
    }
}

double GPU_IncrementalIntegrator::stopTiming()
{
    if (profilingEnabled) {
        auto endTime = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);
        return duration.count() / 1000.0; // Convert to milliseconds
    }
    return 0.0;
}